﻿#include <opencv2/imgproc/types_c.h>

// #include <fstream>
// #include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "exif.h"
#include "facedetectcnn.h"

using namespace std;
using namespace cv;
#define DETECT_BUFFER_SIZE 0x20000
#define ALGO_W (640.0)
#define ALGO_H (640.0)

//人脸剪裁区域扩展倍数
#define FACE_MUTLI (3.0f)

enum ImageOrientation {
    IMAGE_ORIENTATION_TL = 1,  ///< Horizontal (normal)
    IMAGE_ORIENTATION_TR = 2,  ///< Mirrored horizontal
    IMAGE_ORIENTATION_BR = 3,  ///< Rotate 180
    IMAGE_ORIENTATION_BL = 4,  ///< Mirrored vertical
    IMAGE_ORIENTATION_LT = 5,  ///< Mirrored horizontal & rotate 270 CW
    IMAGE_ORIENTATION_RT = 6,  ///< Rotate 90 CW
    IMAGE_ORIENTATION_RB = 7,  ///< Mirrored horizontal & rotate 90 CW
    IMAGE_ORIENTATION_LB = 8   ///< Rotate 270 CW
};

int ResizeOrig(const unsigned char* imgData, const int imglen, cv::Mat& cvimg) {
    easyexif::EXIFInfo result;
    int code = result.parseFrom(imgData, imglen);
    if (code) {
        // std::cout << "EXIFInfo Error:" << code << std::endl;
    }
    std::string img_str((char*)imgData, imglen);
    std::vector<char> data(img_str.begin(), img_str.end());
    cv::Mat img = cv::imdecode(data, 1);
    std::cout << "result.Orientation:" << result.Orientation << std::endl;
    switch (result.Orientation) {
    case IMAGE_ORIENTATION_TL:
        break;
    case IMAGE_ORIENTATION_TR:  // 0th row == visual top, 0th column == visual
                                // right-hand side
        flip(img, img, 1);      // flip horizontally
        break;
    case IMAGE_ORIENTATION_BR:  // 0th row == visual bottom, 0th column ==
                                // visual right-hand side
        flip(img, img, -1);     // flip both horizontally and vertically
        break;
    case IMAGE_ORIENTATION_BL:  // 0th row == visual bottom, 0th column ==
                                // visual left-hand side
        flip(img, img, 0);      // flip vertically
        break;
    case IMAGE_ORIENTATION_LT:  // 0th row == visual left-hand side, 0th column
                                // == visual top
        transpose(img, img);
        break;
    case IMAGE_ORIENTATION_RT:  // 0th row == visual right-hand side, 0th column
                                // == visual top
        transpose(img, img);
        flip(img, img, 1);  // flip horizontally
        break;
    case IMAGE_ORIENTATION_RB:  // 0th row == visual right-hand side, 0th column
                                // == visual bottom
        transpose(img, img);
        flip(img, img, -1);  // flip both horizontally and vertically
        break;
    case IMAGE_ORIENTATION_LB:  // 0th row == visual left-hand side, 0th column
                                // == visual bottom
        transpose(img, img);
        flip(img, img, 0);  // flip vertically
        break;
    default:
        break;
    }

    double scale = 1.0, scalew = 1.0, scaleh = 1.0;
    if (img.cols > ALGO_W) {
        scalew = ALGO_W / img.cols;
    }
    if (img.rows > ALGO_H) {
        scaleh = ALGO_H / img.rows;
    }
    scale = scalew < scaleh ? scalew : scaleh;
    Size ResImgSiz = Size(img.cols * scale, img.rows * scale);
    cvimg = Mat(ResImgSiz, img.type());
    resize(img, cvimg, ResImgSiz, 3);

    // std::cout << cvimg.cols << std::endl;
    // std::cout << cvimg.rows << std::endl;

    return 0;
}
int ReCalcRect(cv::Rect& rc, int MaxW, int MaxH, float multi) {
    // std::cout << "in" << std::endl;
    // std::cout << rc.x << "-" << rc.y << std::endl;
    // std::cout << rc.width << "-" << rc.height << std::endl;

    int CenterX = rc.x + rc.width / 2;
    int CenterY = rc.y + rc.height / 2;

    int exW = multi * rc.width;
    int exH = multi * rc.height;

    rc.x = (CenterX > exW / 2) ? (CenterX - exW / 2) : 0;
    rc.y = (CenterY > exH / 2) ? (CenterY - exH / 2) : 0;

    int exEndX = CenterX + exW / 2;
    int exEndY = CenterY + exH / 2;
    exEndX = (exEndX > MaxW) ? MaxW : exEndX;
    exEndY = (exEndY > MaxH) ? MaxH : exEndY;

    rc.width = exEndX - rc.x;
    rc.height = exEndY - rc.y;

    // std::cout << "out" << std::endl;
    // std::cout << rc.x << "-" << rc.y << std::endl;
    // std::cout << rc.width << "-" << rc.height << std::endl;
    return 0;
}
int FaceDetect(unsigned char* imgData, long& imglen) {
    int faceCount = 0;
    int faceconfidence = 0;
    Mat image;
    ResizeOrig(imgData, imglen, image);
    if (image.empty()) {
        return -1;
    }
    int* pResults = NULL;
    unsigned char* pBuffer = (unsigned char*)malloc(DETECT_BUFFER_SIZE);
    if (!pBuffer) {
        fprintf(stderr, "Can not alloc buffer.\n");
        return -1;
    }
    pResults = facedetect_cnn(pBuffer, (unsigned char*)(image.ptr(0)),
                              image.cols, image.rows, (int)image.step);
    Mat result_image = image.clone();
    cv::Rect faceRc;
    for (int i = 0; i < (pResults ? *pResults : 0); i++) {
        short* p = ((short*)(pResults + 1)) + 142 * i;
        int confidence = p[0];
        faceCount++;
        if (confidence >= 99) {
            faceconfidence = confidence;
            faceRc = Rect(p[1], p[2], p[3], p[4]);
        }
    }
    if (faceCount == 1 && faceconfidence >= 99) {
        ReCalcRect(faceRc, result_image.cols, result_image.rows, FACE_MUTLI);
        Mat faceImage(result_image, faceRc);
        cv::imwrite("face.jpg", faceImage);
        std::vector<uchar> data_encode;
        imencode(".jpg", faceImage, data_encode);
        std::string str_encode(data_encode.begin(), data_encode.end());
        memcpy(imgData, str_encode.c_str(), str_encode.length());
        imglen = str_encode.length();
    }
    free(pBuffer);
    return faceCount;
}

// int main(int argc, char** argv) {
//     if (argc != 2) {
//         printf("Usage: %s <image_file_name>\n", argv[0]);
//         return -1;
//     }
//     long MaxJpgLen = 2 * 1024 * 1024;
//     char* pJpgData = (char*)malloc(MaxJpgLen);
//     long JpgLen = ReadFile(argv[1], pJpgData, MaxJpgLen);
//     int FaceCount = FaceDetect((unsigned char*)pJpgData, JpgLen);
//     std::cout << "FaceDetect count:" << FaceCount << std::endl;
//     if (FaceCount == 1) {
//         WriteFile("result_face.jpg", pJpgData, JpgLen);
//     }
//     free(pJpgData);
//     return 0;
// }